﻿using Launchkit_Backend.model;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Launchkit_Backend
{
    public partial class Form1 : Form
    {
        #region base Ui effect
        private int targetTop, targetLeft;

        private void calcWindowPosition() {
            targetTop = Screen.FromPoint(Control.MousePosition).WorkingArea.Bottom - this.Height;
            targetLeft = Screen.FromPoint(Control.MousePosition).WorkingArea.Left;
        }

        public Form1()
        {
            InitializeComponent();
            calcWindowPosition();
            this.Icon = Properties.Resources.icon;
            this.Top = targetTop;
            this.Left = targetLeft;
            this.DoubleBuffered = true;//设置本窗体
            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true); // 禁止擦除背景.
            SetStyle(ControlStyles.DoubleBuffer, true); // 双缓冲
        }

        private void Form1_ResizeEnd(object sender, EventArgs e)
        {
            if (this.WindowState != FormWindowState.Minimized)
            {
                movingInternalTop = this.Top;
                movingInternalLeft = this.Left;
                calcWindowPosition();
                repositionTimer.Enabled = true;
            }
        }
        private float movingInternalTop, movingInternalLeft;

        private void repositionTimer_Tick(object sender, EventArgs e)
        {
            float deltaX = (movingInternalLeft - targetLeft) * 0.2f;
            float deltaY = (movingInternalTop - targetTop) * 0.2f;
            movingInternalLeft -= deltaX;
            movingInternalTop -= deltaY;

            this.Top = (int)movingInternalTop;
            this.Left = (int)movingInternalLeft;

            if (Math.Max(Math.Abs(deltaX), Math.Abs(deltaY)) < 0.3) {
                repositionTimer.Enabled = false;
                this.Top = targetTop;
                this.Left = targetLeft;
            }
        }


        private int lastDay=-2;
        private int lastSecond = -2;
        String dateCache = "";
        private void updateTimeTimer_Tick(object sender, EventArgs e)
        {
            DateTime dt = DateTime.Now;
            if (dt.Day != lastDay) {
                dateCache = dt.ToString("yyyy\\年MM\\月dd\\日")+" ";
                lunarCalendar = new System.Globalization.ChineseLunisolarCalendar();

                
                int year = lunarCalendar.GetYear(DateTime.Now);
                int day = lunarCalendar.GetDayOfMonth(DateTime.Now);
                int month = lunarCalendar.GetMonth(DateTime.Now);
                int leapMonth = lunarCalendar.GetLeapMonth(year);
                string date = string.Format("{0}{1}年{2}{3}月{4}{5}"
         , "甲乙丙丁戊己庚辛壬癸"[(year - 4) % 10]
         , "子丑寅卯辰巳午未申酉戌亥"[(year - 4) % 12]
         , month == leapMonth ? "闰" : ""
         , "无正二三四五六七八九十冬腊"[leapMonth > 0 && leapMonth <= month ? month - 1 : month]
         , "初十廿三"[day / 10]
         , "十一二三四五六七八九"[day % 10]
         );
                dateCache += date;
                lastDay = dt.Day;
            }

            if (lastSecond != dt.Second) {

                lblTime.Text = dateCache + "  " + dt.ToString("HH\\:mm\\:ss");

                lastSecond = dt.Second;
            }
        }
        System.Globalization.ChineseLunisolarCalendar lunarCalendar;

        private void initGreetingAndTime() {
            String msg = "早上好";
            int hour = DateTime.Now.Hour;
            if (hour < 5 || hour > 22) {
                msg = "夜深了";
            }
            if (hour > 8) {
                msg = "上午好";
            }
            if (hour > 11)
            {
                msg = "中午好";
            }
            if (hour > 12)
            {
                msg = "下午好";
            }
            if (hour > 17)
            {
                msg = "晚上好";
            }
            lblGreeting.Text = msg + "，" + Environment.GetEnvironmentVariable("username");
            updateTimeTimer_Tick(null, null);
        }

        protected override void WndProc(ref Message m)
        {
            if (m.Msg == 0x0014) {//WM_CLEAR_BACKGROYUND
                return;
            }
            base.WndProc(ref m);
        }

        #endregion


        #region baseEntryManagement
        Entry currentDirectory;
        List<Entry> currentEntries;
        List<Entry> path = new List<Entry>();

        List<Entry> backlog = new List<Entry>();
        List<Entry> forwardlog = new List<Entry>();


        public void pwd() {
            path.Clear();
            Entry e = currentDirectory;
            path.Insert(0, e);
            while(e.ID!=0)
            {
                e = DBUtils.getEntryById(e.ParentID);
                path.Insert(0, e);
            }
        }

        public void ls() {
            currentEntries = currentDirectory.ListEntries();
        }

        public void cd(Entry e) {
            currentDirectory = e;
            ls();
            pwd();
        }

        #endregion

        #region Controls
        private void Form1_Load(object sender, EventArgs e)
        {
            initGreetingAndTime();
            cd(DBUtils.getEntryById(0));
            reprocessBackground();
            loadData();
            Toast = new Toast(this);
        }

        public Toast Toast;

        void reprocessBackground() {
            Image realBg = new Bitmap(Properties.Resources.defaultBg, ClientSize);
            Graphics g = Graphics.FromImage(realBg);
            Brush b = new SolidBrush(Color.FromArgb(0x7f000000));
            g.FillRectangle(b, panelMain.Top, panelMain.Left, panelMain.Width, panelMain.Height);
            Point location =Point.Subtract(tblMain.PointToScreen(Point.Empty),new Size(this.PointToScreen(Point.Empty)))  ;
            Console.WriteLine(location);
            g.FillRectangle(b, location.X, location.Y, tblMain.Width, tblMain.Height);
            g.Dispose();
            this.BackgroundImage = realBg;
        }

        private void btnAdd_Click(object sender, EventArgs e)
        {
            mnuAdd.Show(MousePosition);
        }


        bool[,] boundingMap = new bool[CtlTile.WrapperWidth,CtlTile.WrapperHeight];

        public void loadData() {
            tblAddress.SuspendLayout();
            tblAddressInner.SuspendLayout();
            tblAddressInner.Controls.Clear();
            for (int i = path.Count-1; i >=0; i--)
            {
                Entry bindPath = path[i];
                BorderlessButton pathbtn = new BorderlessButton();
                pathbtn.AutoSize = true;
                pathbtn.Text = bindPath.DisplayName;
                pathbtn.Tag = bindPath.ID;
                pathbtn.Image = Properties.Resources.pathseparator;
                pathbtn.ImageAlign = ContentAlignment.MiddleLeft;
                pathbtn.Click += Pathbtn_Click;
                tblAddressInner.Controls.Add(pathbtn);
            }
            tblAddress.ResumeLayout();
            tblAddressInner.ResumeLayout();
            tblMain.SuspendLayout();
            tblMain.Visible = false;
            tblMain.Controls.Clear();
            tileMapRemove(0, 0, CtlTile.WrapperWidth, CtlTile.WrapperHeight);
            foreach (Entry entry in currentEntries)
            {
                CtlTile tile = new CtlTile();
                tblMain.Controls.Add(tile);
                tile.bindToEntry(entry);
                tile.MouseDown += Tile_MouseDown;
                tile.MouseMove += Tile_MouseMove;
                tile.MouseUp += Tile_MouseUp;
                tileMapAdd(entry);
            }
            tblMain.ResumeLayout();
            lblMainEmpty.Visible = currentEntries.Count == 0;
            tblMain.Visible = true;
            this.Text = currentDirectory.DisplayName + " - LaunchKit";
            if (currentDirectory.ID == 0) {
                if (isHistory) {
                    loadHistory();
                }
                else
                {
                    loadFav();

                }
            }
            if (!DBUtils.isFav(currentDirectory))
            {
                btnLike.Image = Properties.Resources.ic_heart;
            }
            else
            {
                btnLike.Image = Properties.Resources.ic_heart_fill;
            }
        }

        private void Pathbtn_Click(object sender, EventArgs e)
        {
            BorderlessButton btn = (BorderlessButton)sender;
            if(currentDirectory.ID!= (long)btn.Tag){
                NavigateTo(DBUtils.getEntryById((long)btn.Tag));
            }
        }

        public void NavigateTo(Entry e) {
            if (e == null) {
                return;
            }
            backlog.Insert(0, currentDirectory);
            while (backlog.Count > 20)
            {
                backlog.RemoveAt(backlog.Count - 1);
            }
            cd(e);
            loadData();
            forwardlog.Clear();
        }

        #region TileMap Logic

        private void tileMapAdd(Entry e)
        {

            tileMapAdd(e.EntryData.x, e.EntryData.y, e.EntryData.w, e.EntryData.h);
        }
        private void tileMapRemove(Entry e)
        {

            tileMapRemove(e.EntryData.x, e.EntryData.y, e.EntryData.w, e.EntryData.h);
        }
        private bool tileMapCollapse(Entry e)
        {

            return tileMapCollapse(e.EntryData.x, e.EntryData.y, e.EntryData.w, e.EntryData.h);
        }
        private bool tileMapCollapse(int x, int y, int w, int h)
        {
            if (x < 0 || y < 0 || x + w > CtlTile.WrapperWidth || y + h > CtlTile.WrapperHeight)
            {
                return true;
            }
            int _x = x, _y = y;
            for (x = _x; x < _x + w; x++)
            {
                for (y = _y; y < _y + h; y++)
                {
                    if (boundingMap[x, y]) {
                        return true;
                    }
                }
            }
            return false;
        }

        private bool allocatePosition(int w, int h,out Point p) {
            for (int x = 0; x < CtlTile.WrapperWidth; x++)
            {
                for (int y = 0; y < CtlTile.WrapperHeight; y++)
                {
                    if (!tileMapCollapse(x,y,w,h))
                    {
                        p = new Point(x, y);
                        return true;
                    }
                }
            }
            p = Point.Empty;
            return false;
        }

        private void tileMapRemove(int x, int y, int w, int h) {
            tileMapFill(x, y, w, h, false);
        }

        private void tileMapAdd(int x, int y, int w, int h)
        {
            tileMapFill(x, y, w, h, true);
        }
        private void tileMapFill(int x, int y, int w, int h,bool value)
        {
            int _x = x, _y = y;
            for (x=_x; x < _x+w; x++)
            {
                for (y=_y; y < _y+h; y++)
                {
                    boundingMap[x, y] = value;
                }
            }
        }

        #endregion

        private Entry mouseSelectedEntry = null;
        private CtlTile mouseSelectedTile = null;
        private bool isDragTile = false;
        private Point dragBeginPos = Point.Empty;
        private bool mouseDown = false;
        private void Tile_MouseUp(object sender, MouseEventArgs e)
        {
            CtlTile tile = (CtlTile)sender;
            if (e.Button == MouseButtons.Right)
            {
                mouseSelectedEntry = tile.bindEntry;
                mouseSelectedTile = tile;
                mnuItem.Show(tile, e.Location);
            }
            if (e.Button == MouseButtons.Left) {
                if (!isDragTile)
                {
                    tile.PerformAction(this);
                }
                else {
                    tile.saveEntry();
                    tileMapAdd(tile.bindEntry);
                }
                isDragTile = false;
                mouseDown = false;
                
            }
        }

        

        private void mnuResizeClick(object sender, EventArgs e)
        {
            ToolStripMenuItem tsmi = (ToolStripMenuItem)sender;
            string[] strs = tsmi.Text.Split('x');
            int w = int.Parse(strs[0]);
            int h = int.Parse(strs[1]);
            Entry ent = mouseSelectedEntry;
            tileMapRemove(ent);
            int bw = ent.EntryData.w, bh = ent.EntryData.h;
            ent.EntryData.w = w;
            ent.EntryData.h = h;
            if (tileMapCollapse(ent))
            {
                Toast.ShowMessage("没有足够的空间");
                ent.EntryData.w = bw;
                ent.EntryData.h = bh;
            }
            else {
                mouseSelectedTile.W = w;
                mouseSelectedTile.H = h;
                mouseSelectedTile.saveEntry();
            }
            tileMapAdd(ent);
            
        }

        private void 选择图标ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            string newIcon = DlgPickIcon.PickIcon(this);
            if (newIcon != null) {
                mouseSelectedTile.IconName = newIcon;
                mouseSelectedTile.saveEntry();
            }
        }

        private void 打开ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            mouseSelectedTile.PerformAction(this);
        }

        private void 重命名ToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            String str = mouseSelectedTile.Title;
            if (DlgInputBox.InputText(this, "重命名", "请输入文件名", ref str)) {
                if (str.Trim() != "")
                {
                    mouseSelectedTile.Title = str;
                    mouseSelectedTile.saveEntry();
                }
                else {
                    Toast.ShowMessage("请输入有效的名称");
                }
            }
        }

        private void 确认删除ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            tblMain.Controls.Remove(mouseSelectedTile);
            mouseSelectedEntry.Delete();
            Toast.ShowMessage("删除成功");
            tileMapRemove(mouseSelectedEntry);
            if (tblMain.Controls.Count == 0) {
                lblMainEmpty.Visible = true;
            }
        }

        private void tblMain_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetFormats().Any(f => f == DataFormats.FileDrop))
            {
                e.Effect = DragDropEffects.Link;
            }
            else {
                e.Effect = DragDropEffects.None;
            }
        }

        private void tblMain_DragDrop(object sender, DragEventArgs e)
        {
            if (e.Data.GetFormats().Any(f => f == DataFormats.FileDrop))
            {
                string[] localFilePath = ((object[])e.Data.GetData(DataFormats.FileDrop)).Select(o => o.ToString()).ToArray() ;//文件完整路径
                addFiles(localFilePath);
            }
        }

        private void Tile_MouseMove(object sender, MouseEventArgs e)
        {
            CtlTile tile = (CtlTile)sender;
            if (mouseDown) {
                if (isDragTile)
                {
                    Point origin = tblMain.PointToScreen(Point.Empty);
                    Point mousePosition = MousePosition;
                    int realX = mousePosition.X - origin.X - dragBeginPos.X+ CtlTile.CellSize / 2;
                    int realY = mousePosition.Y - origin.Y - dragBeginPos.Y + CtlTile.CellSize / 2;

                    int targetX = realX / CtlTile.CellSize; 
                    int targetY = realY / CtlTile.CellSize;

                    if (!tileMapCollapse(targetX, targetY, tile.W, tile.H)) {
                        tile.X = targetX;
                        tile.Y = targetY;
                    }

                }
                else{
                    if (Math.Abs(e.X - dragBeginPos.X) > 32 || Math.Abs(e.Y - dragBeginPos.Y) > 32) {
                        isDragTile = true;
                        tileMapRemove(tile.bindEntry);
                    }
                }
            }
        }

        private void Tile_MouseDown(object sender, MouseEventArgs e)
        {
            CtlTile tile = (CtlTile)sender;
            if (e.Button == MouseButtons.Left) {
                mouseDown = true;
                mouseSelectedEntry = tile.bindEntry ;
                dragBeginPos = e.Location;

            }
        }

        private void 文件或程序ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (openFileDialog1.ShowDialog() == DialogResult.OK) {
                addFiles(openFileDialog1.FileNames);
            }
        }

        private void 目录ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
            {
                addFiles(new string[] { folderBrowserDialog1.SelectedPath });
            }
        }

        private void 超链接ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            string name = "新建板";
            if (DlgInputBox.InputText(this, "新建", "请输入板的名称", ref name)) {
                if (name.Trim() != "")
                {
                    long id = DBUtils.allocateId();
                    Entry entry = new Entry(currentDirectory.ID, Entry.TypeCollection, name, IconProvider.ic_dir, EntryActions.ActionLink, id.ToString());
                    entry.ID = id;
                    entry.EntryData.w = 2;
                    entry.EntryData.h = 2;
                    addEntryToCurrent(entry);
                }
                else {
                    Toast.ShowMessage("请输入有效的名称");
                }
            }
        }

        private void 执行命令ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            EntryData ed = DlgCreateParam.InputParam(this, "命令行内容 (例如 cmd、shutdown -s -t 0)");
            if (ed != null) {
                Entry entry = new Entry(currentDirectory.ID, Entry.TypeEntry, ed.action, IconProvider.ic_cmd, EntryActions.ActionCommand, ed.param);
                entry.ID = DBUtils.allocateId();
                entry.EntryData.w = 2;
                entry.EntryData.h = 2;
                addEntryToCurrent(entry);
            }
        }

        private void 打开网页ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            EntryData ed = DlgCreateParam.InputParam(this, "网页链接");
            if (ed != null)
            {
                if (!ed.param.Contains("://")) {
                    ed.param = "http://" + ed.param;
                }

                Entry entry = new Entry(currentDirectory.ID, Entry.TypeEntry, ed.action, IconProvider.ic_url, EntryActions.ActionWebsite, ed.param);
                entry.ID = DBUtils.allocateId();
                entry.EntryData.w = 2;
                entry.EntryData.h = 2;
                addEntryToCurrent(entry);
            }

        }

        private void btnHome_Click(object sender, EventArgs e)
        {
            if (currentDirectory.ID != 0)
            {
                NavigateTo(DBUtils.getEntryById(0));
            }
        }

        private void btnBack_Click(object sender, EventArgs e)
        {
            
            if (backlog.Count > 0) {
                forwardlog.Insert(0, currentDirectory);
                while (forwardlog.Count > 20)
                {
                    forwardlog.RemoveAt(forwardlog.Count - 1);
                }
                Entry entry = backlog[0];
                cd(entry);
                loadData();
                backlog.RemoveAt(0);
            }
        }

        private void btnForward_Click(object sender, EventArgs e)
        {
            
            if (forwardlog.Count > 0)
            {
                backlog.Insert(0, currentDirectory);
                while (backlog.Count > 20)
                {
                    backlog.RemoveAt(backlog.Count - 1);
                }
                Entry entry = forwardlog[0];
                cd(entry);
                loadData(); 
                forwardlog.RemoveAt(0);
            }
        }

        private void btnLike_Click(object sender, EventArgs e)
        {
            if (DBUtils.isFav(currentDirectory)) {
                DBUtils.removeFav(currentDirectory);
                btnLike.Image = Properties.Resources.ic_heart;
            }
            else
            {
                DBUtils.addFav(currentDirectory);

                btnLike.Image = Properties.Resources.ic_heart_fill;
            }

            if (isHistory == false) {
                loadFav();
            }
        }

        bool isHistory = true;

        public void clearLeftList() {
            tblLeft.Controls.Clear();
            lblLeftEmptry.Visible = true;
        }

        public void addToLeftList(LeftListItem i) {
            BorderlessButton btn = new BorderlessButton();
            btn.Height = 32;
            btn.Width = tblLeft.Width - 30;
            btn.TextAlign = ContentAlignment.MiddleLeft;
            btn.Text = "　　　" + i.DisplayName;
            btn.ImageAlign = ContentAlignment.MiddleLeft;
            btn.Image = new Bitmap(Program.IconProvider.GetIcon(i.DisplayIcon),24,24);
            btn.Tag = i.EntryId;
            btn.Click += Btn_Click;
            tblLeft.Controls.Add(btn);
            lblLeftEmptry.Visible = false;
        }


        private void Btn_Click(object sender, EventArgs e)
        {
            BorderlessButton btn = (BorderlessButton)sender;
            Entry entry = DBUtils.getEntryById((long)btn.Tag);
            
            if (entry.Type == Entry.TypeCollection)
            {
                if (currentDirectory.ID != (long)btn.Tag)
                {
                    NavigateTo(DBUtils.getEntryById((long)btn.Tag));
                }
            }
            else
            {
                if (EntryActions.actions.ContainsKey(entry.EntryData.action)) {
                    EntryActions.actions[entry.EntryData.action](this, entry.EntryData.param);
                }
            }
        }

        private void btnRecent_Click(object sender, EventArgs e)
        {
            isHistory = true;
            loadHistory();
        }

        private void btnFav_Click(object sender, EventArgs e)
        {
            isHistory = false;
            loadFav();
        }


        private void loadHistory() {
            tblLeft.Visible = false;
            clearLeftList();
            DBUtils.getHistory().ForEach(addToLeftList);
            tblLeft.Visible = true;
        }

        private void loadFav()
        {
            tblLeft.Visible = false;
            clearLeftList();
            DBUtils.getFav().ForEach(addToLeftList);
            tblLeft.Visible = true;
        }


        #endregion

        #region AddEntry

        public void addFiles(string[] file) {
            long initialId = DBUtils.allocateId();
            foreach (string item in file)
            {
                String f = Path.GetFullPath(item);
                String name = "";
                String icon = "";
                if (Directory.Exists(f))
                {

                    name = Path.GetFileNameWithoutExtension(f);
                    icon = IconProvider.ic_dir;
                }
                else {
                    name = Path.GetFileNameWithoutExtension(f);
                    icon = f;
                }
                Entry e = new Entry(currentDirectory.ID, Entry.TypeEntry, name, icon, EntryActions.ActionOpenFile, f);
                e.ID = initialId;
                e.EntryData.w = 2;
                e.EntryData.h = 2;
                if (!addEntryToCurrent(e)) {
                    break;
                }


                initialId++;
            }


        }

        public bool addEntryToCurrent(Entry e) {
            e.ParentID = currentDirectory.ID;
            Point p0 = Point.Empty;
            if (allocatePosition(e.EntryData.w, e.EntryData.h, out p0))
            {
                e.EntryData.x = p0.X;
                e.EntryData.y = p0.Y;
                CtlTile tile = new CtlTile();
                tblMain.Controls.Add(tile);
                tile.bindToEntry(e);
                tile.MouseDown += Tile_MouseDown;
                tile.MouseMove += Tile_MouseMove;
                tile.MouseUp += Tile_MouseUp;
                tileMapAdd(e);
                e.Save();
                lblMainEmpty.Visible = false;
                return true;
            }
            else
            {
                Toast.ShowMessage("空间不足,部分块未能放置");
                return false;
            }
        }

        #endregion

    }
}
